import collections
import pickle as pk
import os.path

from wineAttributes import AttributeHolder, CepagesAtt, SubAreaAtt
from bottle import Bottle

from string import Template
dumpDataStr = Template("""# -*- coding: utf-8 -*- 
dic={
    'name' : "$name" ,
    'localname' : _("$name") ,
    'cepages' : $cepages ,
    'areas'   : """)

class Area(CepagesAtt, SubAreaAtt):
    """Base class to describe a  hierarchy of wine regions.

    Area defines a tree structures with a parentArea and sub-areas (ex: a Country and its regions)
    """

    fullId_str = None
    parentArea=None
    subAreaClass = None
    name = ""
    localname = ""
    wines = []
    nWinesTotal = 0
    hasUserContent = False
    _interActiveArea = True
    activeSelectors = []

    _noSelUpdate = False    

    _debugPrefix = '_'
    
    def __init__(self, name, parentArea = None, localname= '', systemFile = '', userFile='', **args):
        self.name = name
        self.parentArea = parentArea
        self.localname = localname 
        if systemFile :
            self.loadContent( systemFile, user = False)
        if userFile :
            self.loadContent( userFile, user = True)

    
    def dataPath(self):
        n = self.name.replace(' ','_')
        p = self.parentArea.dataPath()
        return p+'_'+n

    def dumpSystemSubArea(self):
        s =  unicode(self._system_subAreas.keys()) +'\n'
        
        return s.replace(',', ',\n')

    def dumpSystemData(self):
        
        dpath = self.dataPath()
        ds = dumpDataStr.substitute(name=self.name, cepages = str(self._system_cepages))
        ds += self.dumpSystemSubArea()
        ds += '}'
        f = codecs.open(dpath+'.py', 'w',encoding='utf-8')
        f.write(ds)
        f.close()
        for a in self._system_subAreas.values():
            a.dumpSystemData()

    def readSystemDataFromFile(self, dpath):
        print 'readSystemDataFromFile -- > ', dpath
        if not os.path.exists( dpath ):
            # build an empty area
            self.localname = self.name
            self._build_cepages(False)
            self._build_subAreas(False)
            return {}
        d= {}            
        execfile(dpath, globals(), d)
        print d.keys()
        return d


    def readSystemData(self):
        dpath = self.dataPath()+'.py'
        d= self.readSystemDataFromFile(dpath)
        print '  readSystemData from ',dpath , self, d
        if d == {}:
            return
        dic = d['dic']        
        self._system_cepages = dic['cepages']
        self.localname = dic['localname']
        print ' reading ',self.name, self
        for a in dic['areas']:
            area = self.subAreaClass(name = a, parentArea=self)
            area.readSystemData()
            self.addSubArea(area,addUserArea=False)
        return dic
    
            
    def requestSaveContent(self, filename=''):
        self.parentArea.requestSaveContent(filename) # for now just forward to parent

    def saveUserContent(self, filename):
        self.saveContent(filename, user=True)
        
    def saveContent(self, filename, user = False):
        # dump to a string so that if it fails, it doesn't destroy the file
        print " !!!!!!! "
        print " SAVED to ", filename
        print " !!!!!!! "        
        s = pk.dumps( self.toDict(user=user)  , 0 )

        f = open(filename, 'w')
        f.write(s)
        f.close()

    def loadContent(self, filename, user=True):
        print ' LOADING ', filename
        d = pk.load(  open(filename,) )
        if user:
            self._user_content = filename
        else:
            self._system_content = filename
        self.fromDict(d, user=user)


    def toDict(self,user=False):
        d ={}
        d['hasUserContent'] = self.hasUserContent
        d['wines'] = [w.toDict() for w in self.wines]
        d['localname'] = self.localname

        for att in Area.__bases__:
            if issubclass(att, AttributeHolder):
                d.update(att.toDict(self,user) )
        return d

    def fromDict(self,d,user=False):
        if not user :
            #print ' fromDict ', self.name, d.keys(), d.get('localname',None)            
            self.localname = d.get('localname', self.name) 
        self.hasUserContent = d.pop('hasUserContent')
        #if user : print self._debugPrefix,' fromDict ', self.name , '   ',d['wines']
        self.wines = [Bottle.buildFromDict(wd, self) for wd in d.pop('wines')]
        for att in Area.__bases__:
            if issubclass(att, AttributeHolder):
                att.fromDict(self,d,user) 

    def _transferUDataToSData(self,  sa):
        for att in Area.__bases__:
            if issubclass(att, AttributeHolder):
                att._transferUDataToSData(self,sa) 
        for w in self.wines :
            sa.addWine(w)
        self.wines = [] 
        print " xxx transfering from ",self, self.name ," to ", sa.name, " wines = ", self.wines

        

    def areaInheritance(self):
        return [self]+self.parentArea.areaInheritance()

        
    

    def __getstate__(self):
        #print 'pickcling ',self , '  ',self.name ,self.__dict__
        self.__dict__.pop('_user_cepages',None)
        self.__dict__.pop('_user_subAreas',None)        
        return self.__dict__



    def getWines(self):
        """ returns list of user wines in this area"""
        return [ v for v in self.iterWines() ]
    
    def iterWines(self):
        """iterator over user wines in this area"""
        #print 'iterating  ', self, self.name, self.wines
        if self.wines:
            for w in self.wines:
                yield w
        for a in self.subAreas():
            for w in a.iterWines():
                yield w                

    def iterTastings(self):
        """iterator over tastings of user wines in this area"""
        for w in self.iterWines():
            if w.tastings:
                for t in w.tastings:
                    yield t
        
    
    def addWine(self, w):
        w.parentArea = self
        if self.wines == []:
            self.wines = [w]
        else:
            self.wines.append(w)
        self.flagUserContent()

    def getWine(self, wname, millesime=None):
        for w in self.wines:
            #print 'wines in ', self.name, '   ->', w.cru_name
            if w.cru_name == wname:
                if not millesime:
                    return w
                if millesime == w.millesime:
                    return w
        return None

    def flagUserContent(self):
        self.hasUserContent = True
        if self.parentArea:
            self.parentArea.flagUserContent()

    def dump(self, i=0):
        #print '---'*i, self.name, self.__class__
        for v in self.wines:
            v.dump(i)
        for a in self.subAreas():
            a.dump(i+1)

    def doOnSubAreas(self, f):
        f(self)
        for a in self.subAreas():
            a.doOnSubAreas(f)

    def countNWines(self):
        n = len(self.wines)
        n += sum( a.countNWines() for a in self.subAreas())
        self.nWinesTotal = n
        return n

    def countNBottles(self):
        n = sum( b.numInStock for b in self.wines)
        n += sum( a.countNBottles() for a in self.subAreas())
        return n


    def prepareSelection(self):
        self._isSelected = False
        for a in self.subAreas():
            a.prepareSelection()

    def setSelected(self):
        if not self._isSelected:
            self._isSelected = True
            self.parentArea.setSelected()

    def addSelector(self, selector):
        if not selector in self.activeSelectors:
            self.activeSelectors.append(selector)
        return self.updateSelector()

    def removeAllSelector(self):
        self.activeSelectors = []
        return self.updateSelector()
    
    def removeSelector(self, selector):
        if selector in self.activeSelectors:
            self.activeSelectors.remove(selector)
            return self.updateSelector()

    def blockUpdateSelector(self, block):
        self._noSelUpdate = block
        
    def updateSelector(self):
        if self._noSelUpdate:
            return
        self.prepareSelection()
        allwines = self.getWines()
        if self.activeSelectors == []:
            for w in allwines:
                w.is_visible = True
        for s in self.activeSelectors:
            allwines = [w for w in allwines if s(w) ]
        self.newSelectedWines(allwines)
        return allwines

    def newSelectedWines(self, allwines):
        """ A place holder. Gui/application overwrite and decide what to do """
        pass
    
    def isBottleSelected(self, bottle):
        bottle.is_visible = all( s(bottle) for s in self.activeSelectors)
        return bottle.is_visible


    def sortWines(self):
        self.wines.sort( key= lambda w: (w.cru_name, w.millesime) )

    def getRegionOrAbove(self):
        return self

        
    def compare(self,o,d=collections.defaultdict(list)):
        #print "--"*self.level, ' comp ',self.name
        self.sortWines()
        o.sortWines()
        wineC = len(o.wines) == len(self.wines)
        if not wineC :
            d[self].append('wine keys')
            
        #print "--"*self.level, ' comp ',self.name ,' wines num= ', wineC, ' now wines ' 
        wineC = wineC and all( [w1.compare(w2,d) for (w1,w2) in zip(self.wines, o.wines)] )
        #print "--"*self.level, ' wines = ',wineC , ' now cepages/subregion: '
        l = [att.compare(self,o,d) for att in Area.__bases__ if issubclass(att, AttributeHolder)]
        wineC = wineC and all( l )
        #print "--"*self.level, ' DONE _______',wineC
        return wineC


    def synchronize(self, area):
        for att in Area.__bases__:
            #print ' call synchronize for',att 
            if issubclass(att, AttributeHolder):
                att.synchronize(self,area) 

        pass
